package com.spzx.cart.service.impl;

import com.spzx.cart.api.domain.CartInfo;
import com.spzx.cart.service.ICartService;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.context.SecurityContextHolder;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.security.utils.SecurityUtils;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.api.domain.ProductSkuVo;
import com.spzx.product.api.domain.SkuPriceVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CartServiceImpl implements ICartService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RemoteProductService remoteProductService;

    private String getCartKey(Long userId) {
        return "user:cart:" + userId;
    }
    @Override
    public void addToCart(Long skuId, Integer skuNum) {
        //获取当前登陆用户的id
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        //2.创建Hash结构绑定操作对象（方便对hash进行操作）
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        //判断用户购物车中是否已经存在该商品，如果存在，则数量相加(上限99)，不存在则添加到购物车中
        if(hashOps.hasKey(skuId.toString())){
            //商品存在，数量相加
            CartInfo cartInfo = hashOps.get(skuId.toString());
            int count = cartInfo.getSkuNum() + skuNum;
            cartInfo.setSkuNum(count>99? 99 : count);
            cartInfo.setUpdateTime(new Date());
            hashOps.put(skuId.toString(),cartInfo);
        }else {
            //商品不存在，添加到购物车中
            //判断购物车商品种类（不同SKU）总数大于50件
            Long count = hashOps.size();
            if (++count>50){
                throw new RuntimeException("购物车商品种类超上限");
            }
            //添加到购物车中
            CartInfo cartInfo = new CartInfo();

            //远程调用商品服务获取商品信息
            ProductSkuVo productSkuVo = remoteProductService.getProductSku(skuId, SecurityConstants.INNER).getData();
            if (productSkuVo == null) {
                throw new ServiceException("远程调用商品服务获取SKU商品信息失败");
            }
            //4.2.2 为购物车商品对象属性赋值
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            cartInfo.setCartPrice(productSkuVo.getSalePrice());
            cartInfo.setSkuPrice(productSkuVo.getSalePrice());
            cartInfo.setSkuNum(skuNum > 99 ? 99 : skuNum);
            cartInfo.setThumbImg(productSkuVo.getThumbImg());
            cartInfo.setSkuName(productSkuVo.getSkuName());
            cartInfo.setCreateTime(new Date());
            cartInfo.setUpdateTime(new Date());
            //4.2.3 写会到Redis
            hashOps.put(skuId.toString(), cartInfo);
        }
    }

    @Override
    public List<CartInfo> getCartList() {
        //1.构建当前用户的购物车hash结构key 形式：user:cart:用户ID
        Long userId = SecurityUtils.getUserId();
        String cartKey = this.getCartKey(userId);
        //2.创建购物车绑定hash操作对象 决定操作哪个用户购物车
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        //3.获取hash结构中所有value
        List<CartInfo> cartInfoList = hashOps.values();
        for (CartInfo cartInfo : cartInfoList) {
            ProductSkuVo skuVo = remoteProductService.getProductSku(cartInfo.getSkuId(), SecurityConstants.INNER).getData();
            if (skuVo != null) {
                cartInfo.setSkuPrice(skuVo.getSalePrice());
            }
        }
        //4.按照购物车商品创建时间降序
        cartInfoList.sort((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()));
        // 远程调用商品服务获取SKU最新价格

        return cartInfoList;
    }

    @Override
    public void deleteCart(Long skuId) {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(cartKey);
        if (boundHashOperations.hasKey(skuId.toString())) {
            boundHashOperations.delete(skuId.toString());
        }
    }

    @Override
    public void checkCart(Long skuId, Integer isChecked) {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(cartKey);
        if (boundHashOperations.hasKey(skuId.toString())) {
            CartInfo cartInfo = (CartInfo) boundHashOperations.get(skuId.toString());
            cartInfo.setIsChecked(isChecked);
            boundHashOperations.put(skuId.toString(), cartInfo);
        }

    }

    @Override
    public void allCheckCart(Integer isChecked) {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(cartKey);
        if (!CollectionUtils.isEmpty(boundHashOperations.values())) {
            boundHashOperations.values().forEach(cartInfo -> {
                CartInfo cartInfo1 = (CartInfo) cartInfo;
                cartInfo1.setIsChecked(isChecked);
                boundHashOperations.put(cartInfo1.getSkuId().toString(), cartInfo1);
            });
        }
    }

    @Override
    public void clearCart() {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = getCartKey(userId);
        BoundHashOperations boundHashOperations = redisTemplate.boundHashOps(cartKey);
        boundHashOperations.keys().forEach(key -> {
            boundHashOperations.delete(key);
        });
    }

    @Override
    public List<CartInfo> getCartCheckedList() {
        Long userId = SecurityContextHolder.getUserId();
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartCachInfoList = redisTemplate.opsForHash().values(cartKey);
        List<CartInfo> cartInfoList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(cartCachInfoList)){
            cartInfoList = cartCachInfoList.stream().filter(cartInfo ->
                cartInfo.getIsChecked().intValue() == 1
            ).collect(Collectors.toList());

        }
        return cartInfoList;
    }

    @Override
    public Boolean updateCartPrice(Long userId) {
        String cartKey = this.getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        if (!CollectionUtils.isEmpty(cartInfoList)){
            cartInfoList
                    .stream()
                    .filter(cartInfo ->cartInfo.getIsChecked().intValue()==1)
                    .forEach(cartInfo -> {
                        R<SkuPriceVo> skuPrice = remoteProductService.getSkuPrice(cartInfo.getSkuId(), SecurityConstants.INNER);
                        if (R.SUCCESS == skuPrice.getCode()){
                            SkuPriceVo skuPriceVo = skuPrice.getData();
                            cartInfo.setSkuPrice(skuPriceVo.getSalePrice());
                            hashOps.put(cartInfo.getSkuId().toString(),cartInfo);
                        }
                    });
        }
        return true;
    }

    @Override
    public Boolean deleteCartCheckedList(Long userId) {
        String cartKey = this.getCartKey(userId);
        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
        List<CartInfo> cartInfoList = hashOps.values();
        if (!CollectionUtils.isEmpty(cartInfoList)){
            cartInfoList
                    .stream()
                    .filter(cartInfo ->cartInfo.getIsChecked().intValue()==1)
                    //映射获取商品SKUID
                    .map(CartInfo::getSkuId)
                    //遍历删除 选中商品
                    .forEach(skuId -> hashOps.delete(skuId.toString()));
        }
        return true;
    }
}
